package org.quartz.impl.jdbcjobstore;

import java.sql.Connection;
import java.sql.SQLException;

import org.quartz.JobPersistenceException;
import org.quartz.SchedulerConfigException;
import org.quartz.spi.ClassLoadHelper;
import org.quartz.spi.SchedulerSignaler;
import org.quartz.utils.DBConnectionManager;

public class JobStoreCMT extends JobStoreSupport {

	protected String nonManagedTxDsName;

	// Great name huh?
	protected boolean dontSetNonManagedTXConnectionAutoCommitFalse = false;

	protected boolean setTxIsolationLevelReadCommitted = false;


	/**
	 * <p>
	 * Set the name of the <code>DataSource</code> that should be used for
	 * performing database functions.
	 * </p>
	 */
	public void setNonManagedTXDataSource(String nonManagedTxDsName) {
		this.nonManagedTxDsName = nonManagedTxDsName;
	}

	/**
	 * <p>
	 * Get the name of the <code>DataSource</code> that should be used for
	 * performing database functions.
	 * </p>
	 */
	public String getNonManagedTXDataSource() {
		return nonManagedTxDsName;
	}

	public boolean isDontSetNonManagedTXConnectionAutoCommitFalse() {
		return dontSetNonManagedTXConnectionAutoCommitFalse;
	}

	/**
	 * Don't call set autocommit(false) on connections obtained from the DataSource.
	 * This can be helpfull in a few situations, such as if you have a driver that
	 * complains if it is called when it is already off.
	 * 
	 * @param b
	 */
	public void setDontSetNonManagedTXConnectionAutoCommitFalse(boolean b) {
		dontSetNonManagedTXConnectionAutoCommitFalse = b;
	}

	public boolean isTxIsolationLevelReadCommitted() {
		return setTxIsolationLevelReadCommitted;
	}

	/**
	 * Set the transaction isolation level of DB connections to sequential.
	 * 
	 * @param b
	 */
	public void setTxIsolationLevelReadCommitted(boolean b) {
		setTxIsolationLevelReadCommitted = b;
	}

	public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) throws SchedulerConfigException {

		if (nonManagedTxDsName == null) {
			throw new SchedulerConfigException("Non-ManagedTX DataSource name not set!  "
					+ "If your 'org.quartz.jobStore.dataSource' is XA, then set "
					+ "'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA " + "datasource (for the same DB).  "
					+ "Otherwise, you can set them to be the same.");
		}

		if (getLockHandler() == null) {
			// If the user hasn't specified an explicit lock handler,
			// then we *must* use DB locks with CMT...
			setUseDBLocks(true);
		}

		super.initialize(loadHelper, signaler);

		getLog().info("JobStoreCMT initialized.");
	}

	public void shutdown() {

		super.shutdown();

		try {
			DBConnectionManager.getInstance().shutdown(getNonManagedTXDataSource());
		} catch (SQLException sqle) {
			getLog().warn("Database connection shutdown unsuccessful.", sqle);
		}
	}

	protected Connection getNonManagedTXConnection() throws JobPersistenceException {
		Connection conn = null;
		try {
			conn = DBConnectionManager.getInstance().getConnection(getNonManagedTXDataSource());
		} catch (SQLException sqle) {
			throw new JobPersistenceException("Failed to obtain DB connection from data source '"
					+ getNonManagedTXDataSource() + "': " + sqle.toString(), sqle);
		} catch (Throwable e) {
			throw new JobPersistenceException("Failed to obtain DB connection from data source '"
					+ getNonManagedTXDataSource() + "': " + e.toString(), e,
					JobPersistenceException.ERR_PERSISTENCE_CRITICAL_FAILURE);
		}

		if (conn == null) {
			throw new JobPersistenceException(
					"Could not get connection from DataSource '" + getNonManagedTXDataSource() + "'");
		}

		// Protect connection attributes we might change.
		conn = getAttributeRestoringConnection(conn);

		// Set any connection connection attributes we are to override.
		try {
			if (!isDontSetNonManagedTXConnectionAutoCommitFalse()) {
				conn.setAutoCommit(false);
			}

			if (isTxIsolationLevelReadCommitted()) {
				conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
			}
		} catch (SQLException sqle) {
			getLog().warn("Failed to override connection auto commit/transaction isolation.", sqle);
		} catch (Throwable e) {
			try {
				conn.close();
			} catch (Throwable tt) {
			}

			throw new JobPersistenceException("Failure setting up connection.", e);
		}

		return conn;
	}

	protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
		boolean transOwner = false;
		Connection conn = null;
		try {
			if (lockName != null) {
				// If we aren't using db locks, then delay getting DB connection
				// until after acquiring the lock since it isn't needed.
				if (getLockHandler().requiresConnection()) {
					conn = getConnection();
				}

				transOwner = getLockHandler().obtainLock(conn, lockName);
			}

			if (conn == null) {
				conn = getConnection();
			}

			return txCallback.execute(conn);
		} finally {
			try {
				releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner);
			} finally {
				cleanupConnection(conn);
			}
		}
	}
}

// EOF
